﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TGInspect
{
    public enum CylPos
    {
        FWD = 0,
        BWD,
        UNKNOWN,
        NONE,
    }

    interface ICylinder
    {
        void Forward();
        void Backward();
        void Update();
    }

    interface ICylAlarm
    {
        bool Alarm{get; set;}
    }

    public class Cylinder : IUpdate, ICylinder
    {
        protected Input input;          // 입력 인스턴스
        protected Output output;        // 출력 인스턴스
        protected int DelayTime;        // 지연시간후 위치값 업데이트(msec)
        protected int Delaying;         // 지연경과시간(msec)
        protected int UnitTime;         // 업데이트당 단위시간(msec)

        public CylPos Pos { get; protected set;}
        public CylPos Command { get; protected set; }
        //public CylPos Commanded { get; protected set; }

        public Cylinder()
        {
            Setup();
        }

        public void Setup()
        {
            Command = CylPos.NONE;
            Delaying = 0;
        }

        protected void CmdInit(CylPos pos)
        {
            Command = pos;
            Pos = CylPos.UNKNOWN;
            Delaying = 0;
        }

        public virtual void Forward()
        {
        }

        public virtual void Backward()
        {
            
        }
               

        public virtual void Update()
        {
            //Console.WriteLine("update");
            

        }

    }

    public class SingleNoSen : Cylinder
    {
        protected OutNum OutNumFwd;

//        public SingleNoSen() {}

        public SingleNoSen(Output output, OutNum outNumFwd,
            int delayTime = 0, 
            int unitTime = Contents.UnitTime)
        {
            this.output = output;
            this.OutNumFwd = outNumFwd;
            this.UnitTime = unitTime;
            this.DelayTime = this.Delaying = delayTime;
            Update();
        }
 
        public override void Forward()
        {
            CmdInit(CylPos.FWD);
            output.Set(true, OutNumFwd);
            Pos = CylPos.UNKNOWN;
        }

        public override void Backward()
        {
            CmdInit(CylPos.BWD);
            output.Set(false, OutNumFwd);
            Pos = CylPos.UNKNOWN;
        }

        public override void Update()
        {
            base.Update();
            if (Command != CylPos.NONE)
            {
                if (Delaying >= DelayTime)
                {
                    Delaying = 0;
                    Pos = Command;
                    Command = CylPos.NONE;
                }
                else
                {
                    Delaying += UnitTime;
                    Pos = CylPos.UNKNOWN;
                }
            }
        }
    }

    public class SingleFwdSen : SingleNoSen, ICylAlarm
    {
        protected InNum InNumFwd;
        protected int AlarmTime;
        public bool Alarm { get; set; }
 
        public SingleFwdSen(Input input, InNum inNumFwd,
            Output output, OutNum outNumFwd,
            int delayTime = 0, 
            int alarmTime = 3000,
            int unitTime = Contents.UnitTime)
            : base(output, outNumFwd, delayTime, unitTime)
        {
            this.input = input;
            this.InNumFwd = inNumFwd;
            this.AlarmTime = alarmTime;

            if (this.input.Get(InNumFwd)) Pos = CylPos.FWD;
            else Pos = CylPos.BWD;
        }

        public override void Forward()
        {
            CmdInit(CylPos.FWD);
            output.Set(true, OutNumFwd);
            Pos = CylPos.UNKNOWN;
        }

        public override void Backward()
        {
            CmdInit(CylPos.BWD);
            output.Set(false, OutNumFwd);
            Pos = CylPos.UNKNOWN;
        }

        public override void Update()
        {
            if (Command == CylPos.FWD)
            {
                if (input.Get(InNumFwd) && Delaying >= DelayTime)
                {
                    Pos = Command;
                    Command = CylPos.NONE;
                }
                else if (Delaying >= AlarmTime)
                {
                    Backward();
                    Alarm = true;
                }
                else
                {
                    Delaying += UnitTime;
                    Pos = CylPos.UNKNOWN;
                }
            }
            else if (Command == CylPos.BWD)
            {
                if (Delaying >= DelayTime)
                {
                    if (input.Get(InNumFwd))
                    {
                        Forward();
                        Alarm = true;
                    }
                    else
                    {
                        Pos = Command;
                        Command = CylPos.NONE;
                    }
                }
                else
                {
                    Delaying += UnitTime;
                    Pos = CylPos.UNKNOWN;
                }
            }
            else
            {
                if (input == null) return;  // Update함수가 생성자에서 한번 실행하는데 베이스 생성자에서 이함수를 실행시켜 오류발생
                
                if (input.Get(InNumFwd))
                    Pos = CylPos.FWD;
                else
                {
                    Pos = CylPos.BWD;
                    if (!output.Get(OutNumFwd))
                        Alarm = true;
                }
            }
        }
    }

    public class SingleFwdBwdSen : SingleFwdSen
    {
        protected InNum InNumBwd;

        public SingleFwdBwdSen(Input input, InNum inNumFwd, InNum inNumBwd,
            Output output, OutNum outNumFwd,
            int delayTime = 0, 
            int alarmTime = 3000,
            int unitTime = Contents.UnitTime)
            : base(input, inNumFwd, output, outNumFwd, delayTime, alarmTime, unitTime)
        {
            this.InNumBwd = inNumBwd;
        }

        public override void Forward()
        {
            CmdInit(CylPos.FWD);
            output.Set(true, OutNumFwd);
            Pos = CylPos.UNKNOWN;
        }

        public override void Backward()
        {
            CmdInit(CylPos.BWD);
            output.Set(false, OutNumFwd);
            Pos = CylPos.UNKNOWN;
        }

        public override void Update()
        {
            if (Command == CylPos.FWD)
            {
                if (input.Get(InNumFwd) && Delaying >= DelayTime)
                {
                    Pos = Command;
                    Command = CylPos.NONE;
                }
                else if (Delaying >= AlarmTime)
                {
                    Backward();
                    Alarm = true;
                }
                else
                {
                    Delaying += UnitTime;
                    Pos = CylPos.UNKNOWN;
                }
            }
            else if (Command == CylPos.BWD)
            {
                if (input.Get(InNumBwd) && Delaying >= DelayTime)
                {
                    Pos = Command;
                    Command = CylPos.NONE;
                }
                else if (Delaying >= AlarmTime)
                {
                    Forward();
                    Alarm = true;
                }
                else
                {
                    Delaying += UnitTime;
                    Pos = CylPos.UNKNOWN;
                }
            }
            else
            {
                if (input == null) return;  // Update함수가 생성자에서 한번 실행하는데 베이스 생성자에서 이함수를 실행시켜 오류발생

                if (input.Get(InNumFwd))
                {
                    Pos = CylPos.FWD;
                    Delaying = 0;
                }
                else if (input.Get(InNumBwd))
                {
                    Pos = CylPos.BWD;
                    Delaying = 0;
                }
                else
                {
                    if(Delaying>= AlarmTime)
                        Alarm = true;
                }
            }
        }
    }


    public class DoubleNoSen : SingleNoSen
    {
        protected OutNum OutNumBwd;
        protected int OffTime, Offing;

        public DoubleNoSen(Output output, OutNum outNumFwd, OutNum outNumBwd, 
             int delayTime = 0, int offTime = 0,
             int unitTime = Contents.UnitTime)
            : base(output, outNumFwd, delayTime, unitTime)
        {
            this.OutNumBwd = outNumBwd;
            this.OffTime = Offing = offTime;
        }
        
        public override void Forward()
        {
            CmdInit(CylPos.FWD);
            output.Set(true, OutNumFwd);
            output.Set(false, OutNumBwd);
            Offing = 0;
        }

        public override void Backward()
        {
            CmdInit(CylPos.BWD);
            output.Set(false, OutNumFwd);
            output.Set(true, OutNumBwd);
            Offing = 0;
        }

        public override void Update()
        {
            base.Update();
            if (OffTime !=0)
            {
                if(Offing >= OffTime)
                {
                    output.Set(false, OutNumFwd);
                    output.Set(false, OutNumBwd);
                }
                else
                    Offing += UnitTime;
            }
        }
    }
    
    
}
